/**
 * This software was produced for the U. S. Government
 * under Contract No. FA8721-09-C-0002, and is
 * subject to the Rights in Noncommercial Computer Software
 * and Noncommercial Computer Software Documentation Clause
 * (DFARS) 252.227-7014 (JUN 1995)
 * 
 * (c) 2009 The MITRE Corporation. All Rights Reserved.
 *
 * SPMIF to OPNET Translator
 * Author: ewalters
 * Created: 1:08:23 PM, Aug 13, 2007
 * MITRE Corporation
 */
package com.mitre.spmif2opnet.generation;

import com.mitre.spmif2opnet.analysis.ast.Device;
import com.mitre.spmif2opnet.analysis.ast.Node;
import com.mitre.spmif2opnet.analysis.ast.Overhead;



/**
 * This is the class for the finite state machine (DFA) for translating from
 *  monolithic S-PMIF code to separate network/processing tasks in opnet
 */
public class ProcNetworkFSM {

	/**
	 * This are the states and transitions for the state machine 
	 */
	public enum State {
		STARTING  // Starting out on a thread
		{ 
			public State process(Letter l, GenerationContext c) throws GenerationException{
				State res = this;
				GraphConverter cv = c.getConverter();
				switch(l) {
				case PROCESS:
				case PROCESS_DIFF:
					// Generate a loop processing task based on the start node
					cv.genProcessingLoop(c, cv.makeResource(c));
					res = State.PROCESSING;
					break;
				case NETWORK:
					// S-PMIF must start with processing elements to make sense!
					res = State.ERROR;
					break;
				case BOTH:
				case BOTH_DIFF:	
					// Generate a processing task (maybe with loop), and an outgoing network task
					//  to "indeterminate"
					String start = cv.makeResource(c);
					cv.genProcessingLoop(c, start);
					cv.genNetworkingStart(c, start);
					res = State.NETWORKING;
					break;
				}
				return res;
			}
		},
		PROCESSING  // Currently processing on a device
		{ 
			public State process(Letter l, GenerationContext c) throws GenerationException{
				GraphConverter cv = c.getConverter();
				State res = this;
				String start, end;
				switch(l) {
				case PROCESS:
					// Loop processing task
					cv.genProcessingLoop(c, cv.getResource(c));
					break;
				case PROCESS_DIFF:
					// Processing task to >different< node
					start = cv.getResource(c);
					end = cv.makeResource(c);
					cv.genNetworking(c, start, end);  // S.B. No resources here!
					cv.genProcessingLoop(c, end);
					break;
				case NETWORK:
					start = cv.getResource(c);
					cv.genNetworkingStart(c, start);
					res = State.NETWORKING;
					break;
				case BOTH:
					// Gen a proc loop on the current node, followed by a networking elem
					start = cv.getResource(c);
					cv.genProcessingLoop(c, start);
					cv.genNetworkingStart(c, start);
					res = State.NETWORKING;
					break;
				case BOTH_DIFF:	
					// Gen processing from the current node to the diff node,
					//  along with loop on the new node
					start = cv.getResource(c);
					end = cv.makeResource(c);
					cv.genNetworking(c, start, end); // This wil use resources!
					cv.genProcessingLoop(c, end);
					break;
				}
				return res;
			}
		},
		NETWORKING  // Currently on the network
		{ 
			public State process(Letter l, GenerationContext c) throws GenerationException {
				GraphConverter cv = c.getConverter();
				State res = this;
				String end;
				switch(l) {
				case PROCESS:
				case PROCESS_DIFF:
					// Finish the networking transition, and end with a loop
					end = cv.makeResource(c);
					cv.genNetworkingEnd(c, end);
					cv.genProcessingLoop(c, end);
					res = State.PROCESSING;
					break;
				case NETWORK:
					// Still in the middle of network traffic
					cv.genNetworkingMiddle(c);
					break;
				case BOTH:
					// NOTE: These results are purely a convention - we may have to adjust them 
					//  Proc loop first coming off of network, followed by transition into network
					end = cv.makeResource(c);
					cv.genNetworkingEnd(c, end);
					cv.genProcessingLoop(c, end);
					cv.genNetworkingStart(c, end);
					break;
				case BOTH_DIFF:	
					// Opposite - finish network, and loop on processing
					end = cv.makeResource(c);
					cv.genNetworkingEnd(c, end);
					cv.genProcessingLoop(c, end);
					res = State.PROCESSING;
					break;
				}
				return res;
			}
		},
		ERROR   // Bad state
		{ 
			public State process(Letter l, GenerationContext c) throws GenerationException {
				return State.ERROR;
			}
		};
		
		abstract State process(Letter l, GenerationContext c) throws GenerationException;
	}
	
	/**
	 *  Alphabet of the state machine
	 */
	enum Letter {
		PROCESS,       // S-PMIF Node uses resource that only access processing devices, devices have not changed since last step
		PROCESS_DIFF,  // S-PMIF Node uses resource that only access processing devices, devices have changed since last step
		NETWORK,       // S-PMIF Node uses resource that only access networking devices      
		BOTH,          // S-PMIF Node uses resource that accesss processing and networking devices, processing devices have not changed since last step
		BOTH_DIFF,     // S-PMIF Node uses resource that accesss processing and networking devices, processing devices have changed since last step
		OTHER
	}
		
	/**
	 * Current state
	 */
	private State _state;
	
	/**
	 * Default constructor 
	 */
	public ProcNetworkFSM() {
		this(State.STARTING);
	}
	
	/**
	 * Start the FSM at this state
	 * @param s the state
	 */
	public ProcNetworkFSM(State s) {
		_state = s;
	}
	
	/**
	 * Copy constructor - for recursive traversals
	 * @param p FSM to copy
	 */
	public ProcNetworkFSM(ProcNetworkFSM p) {
		_state = p._state;
	}

	/**
	 * Perform a state transition
	 * @param l the current letter
	 * @param c the context
	 * @throws GenerationException
	 */
	public void process(Letter l, GenerationContext c) throws GenerationException {
		c.setCurrState(this);
		_state = _state.process(l, c);
	}
	
	/**
	 * Get the equivalent devices for a node and create a letter
	 * @param n   the node
	 * @param over the overhead
	 * @param diff is the node different than the previous?
	 * @return the letter to use to advance the FSM
	 */
	public static ProcNetworkFSM.Letter parseLetter(Node n, Overhead over, boolean diff) {
		boolean has_network = false, has_proc = false;
		//if (n.getResources().isEmpty()) has_proc = true;
		for (String res : n.getResources()) {
			for (Device d : over.getDevices(res)) {
				if (d.isNetworkingDevice())      { has_network = true; }
				else if (d.isProcessingDevice()) { has_proc = true; }
			}
		}
		if (has_network) {
			if (has_proc && diff)  return ProcNetworkFSM.Letter.BOTH_DIFF;
			else if (has_proc)     return ProcNetworkFSM.Letter.BOTH;   
			else                   return ProcNetworkFSM.Letter.NETWORK; 
		} 
		else if (has_proc && diff) return ProcNetworkFSM.Letter.PROCESS_DIFF;
		else if (has_proc)         return ProcNetworkFSM.Letter.PROCESS; 
		else                       return ProcNetworkFSM.Letter.OTHER;
	}

}
